package leafedbox.commands
{
	import leafedbox.events.CommandEvent;
	import leafedbox.managers.CommandManager;
	
	import mx.managers.CursorManager;
	import flash.events.Event;

	public class RemoteCommand extends Command
	{
		//----------------------------------------------------------------------
		// Static methods
		//----------------------------------------------------------------------

		/**
		 * Executes a command with the specified parameters.  Parameters that
		 * can be matched to properties of the class are set.  If they cannot be
		 * matched, they are added through <code>setParameter()</code> with the
		 * default method.
		 */
		static public function execute(classRef:Class, params:Object=null,
									   resultHandler:Function=null,
									   faultHandler:Function=null):RemoteCommand
		{
			// Create new command object
			var command : RemoteCommand = new classRef();

			// Assign properties/parameters
			if(params != null) {
				for(var paramName:String in params) {
					if(command.hasOwnProperty(paramName)) {
						command[paramName] = params[paramName];
					}
					else {
						command.setParameter(paramName, params[paramName]);
					}
				}
			}
			
			// Add handlers
			if(resultHandler != null) {
				command.addEventListener(CommandEvent.RESULT, resultHandler);	
			}
			
			if(faultHandler != null) {
				command.addEventListener(CommandEvent.FAULT, faultHandler);	
			}
			
			// Execute command
			command.execute();
			
			return command;
		}


		//----------------------------------------------------------------------
		// Constructor
		//----------------------------------------------------------------------
    	
		/**
		 * Constructor.
		 */
		public function RemoteCommand():void
		{
			super();
		}


		//----------------------------------------------------------------------
		// Properties
		//----------------------------------------------------------------------

		//----------------------------------
		// General
		//----------------------------------

		/** @private */
		protected var parameters : Object = new Object();


		//----------------------------------
		// Show busy cursor
		//----------------------------------

		/** @private */
		protected var _showBusyCursor:Boolean = true;

		/**
		 * A flag stating if the busy cursor should be displayed while waiting for 
		 * a response.
		 */
		public function get showBusyCursor():Boolean
		{
			return _showBusyCursor;
		}

		/** @private */
		public function set showBusyCursor(value:Boolean):void
		{
			if(_showBusyCursor != value) {
				_showBusyCursor = value;
			}
		}


		//----------------------------------------------------------------------
		// Methods
		//----------------------------------------------------------------------

		//----------------------------------
		// Parameters
		//----------------------------------

		/**
		 * Retrieves a parameter.
		 */
		public function getParameter(name:String, method:String="GET"):*
		{
			if(parameters[method] != null) {
				return parameters[method][name];
			} else {
				return null;
			}
		}

		/**
		 * Sets a parameter.
		 */
		public function setParameter(name:String, value:*, method:String="GET"):void
		{
			if(parameters[method] == null) {
				parameters[method] = new Object();
			}

			parameters[method][name] = value;
		}

		/**
		 * Removes a parameter.
		 */
		public function removeParameter(name:String, method:String="GET"):void
		{
			if(parameters[method] != null) {
				delete parameters[method][name];
			}
		}

		/**
		 * Removes all parameters.
		 */
		public function removeAllParameters():void
		{
			parameters = new Object();
		}


		//----------------------------------------------------------------------
		// Methods : Command
		//----------------------------------------------------------------------

		override public function execute():void
		{
			super.execute();
		}
		

		//----------------------------------------------------------------------
		// Methods
		//----------------------------------------------------------------------

		/**
		 * This method is to be overridden by the subclass to handle invokes.
		 */
		protected function invoke(event:CommandEvent):void
		{
			
		}

		/**
		 * This method is to be overridden by the subclass to handle results.
		 */
		protected function result(event:CommandEvent):void
		{
		}

		/**
		 * This method is to be overridden by the subclass to handle faults.
		 */
		protected function fault(event:CommandEvent):void
		{
		}


		/**
		 * Reponds to an invoke event from the service.
		 */
		protected function processInvoke(event:CommandEvent):void
		{
			// Reset status
			_status        = null;
			_statusCode    = "";
			_statusMessage = "";

			if(showBusyCursor) {
				CursorManager.setBusyCursor();
			}

			// Process local callback and dispatch event
			invoke(event);
			dispatchEvent(event);
			CommandManager.getInstance().dispatchEvent(event);
		}

		/**
		 * Responds to a result event from the service.
		 */
		protected function processResult(event:CommandEvent):void
		{
			// Update status
			if(status == null) {
				_status        = Command.STATUS_OK;
				_statusCode    = "";
				_statusMessage = "";
			}

			if(showBusyCursor) {
				CursorManager.removeBusyCursor();
			}

			// Convert to a fault if we have errant status codes
			if(status != Command.STATUS_OK) {
				event = new CommandEvent(CommandEvent.FAULT, event.bubbles, event.cancelable, event.command);
			}
			
			// Process local callback and dispatch event
			result(event);
			dispatchEvent(event);
			CommandManager.getInstance().dispatchEvent(event);
		}
		
		/**
		 * Reponds to a fault event from the service.
		 */
		protected function processFault(event:CommandEvent):void
		{
			// Update status
			if(status == null) {
				_status        = Command.STATUS_ERROR;
				_statusCode    = "UNKNOWN";
				_statusMessage = "An unexpected error has occurred on the server.";
			}
			
			if(showBusyCursor) {
				CursorManager.removeBusyCursor();
			}

			// Process local callback and dispatch event
			fault(event);
			dispatchEvent(event);
			CommandManager.getInstance().dispatchEvent(event);
		}
	}

}